/* 日志器模块
    抽象日志器基类
    派生不同的子类 异步&同步

*/
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "util.hpp"
#include "format.hpp"
#include "level.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <unordered_map>
namespace bitlog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            : _logger_name(logger_name),    
              _limit_level(level),
              _formatter(formatter),
              _sinks(sinks.begin(), sinks.end()) {}
        /*完成构造日志消息对象过程并进行格式化，得到格式化后的日志消息字符串 然后进行输出*/       
        const std::string &name()
        {
            return _logger_name;
        }
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象
            // 判断当前的日志是否达到输出级别
            if (LogLevel::value::DEBUG < _limit_level)
            {
                return;
            }
            // 对format格式化字符串和不定参进行字符串组织 得到日志消息字符串
            va_list ap; // 指向使用 va_start 和 va_arg 函数处理的可变参数列表的指针。
            va_start(ap, fmt);
            char *res; // 指向指针变量的指针，用于存储格式化字符串的结果。
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!" << std::endl;
                return;
            }
            va_end(ap); // 将ap指针置空
            serialize(LogLevel::value::DEBUG, file, line, res);
            free(res); // 不释放会导致泄露
        }
        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {

            // 通过传入的参数构造出一个日志消息对象
            // 判断当前的日志是否达到输出级别
            if (LogLevel::value::INFO < _limit_level)
            {
                return;
            }
            // 对format格式化字符串和不定参进行字符串组织 得到日志消息字符串
            va_list ap; // 指向使用 va_start 和 va_arg 函数处理的可变参数列表的指针。
            va_start(ap, fmt);
            char *res; // 指向指针变量的指针，用于存储格式化字符串的结果。
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!" << std::endl;
                return;
            }
            va_end(ap); // 将ap指针置空
            serialize(LogLevel::value::INFO, file, line, res);
            free(res); // 不释放会导致泄露
        }
        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {

            // 通过传入的参数构造出一个日志消息对象
            // 判断当前的日志是否达到输出级别
            if (LogLevel::value::WARN < _limit_level)
            {
                return;
            }
            // 对format格式化字符串和不定参进行字符串组织 得到日志消息字符串
            va_list ap; // 指向使用 va_start 和 va_arg 函数处理的可变参数列表的指针。
            va_start(ap, fmt);
            char *res; // 指向指针变量的指针，用于存储格式化字符串的结果。
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!" << std::endl;
                return;
            }
            va_end(ap); // 将ap指针置空
            serialize(LogLevel::value::WARN, file, line, res);
            free(res); // 不释放会导致泄露
        }
        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {

            // 通过传入的参数构造出一个日志消息对象
            // 判断当前的日志是否达到输出级别
            if (LogLevel::value::ERROR < _limit_level)
            {
                return;
            }
            // 对format格式化字符串和不定参进行字符串组织 得到日志消息字符串
            va_list ap; // 指向使用 va_start 和 va_arg 函数处理的可变参数列表的指针。
            va_start(ap, fmt);
            char *res; // 指向指针变量的指针，用于存储格式化字符串的结果。
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!" << std::endl;
                return;
            }
            va_end(ap); // 将ap指针置空
            serialize(LogLevel::value::ERROR, file, line, res);
            free(res); // 不释放会导致泄露
        }
        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {

            // 通过传入的参数构造出一个日志消息对象
            // 判断当前的日志是否达到输出级别
            if (LogLevel::value::FATAL < _limit_level)
            {
                return;
            }
            // 对format格式化字符串和不定参进行字符串组织 得到日志消息字符串
            va_list ap; // 指向使用 va_start 和 va_arg 函数处理的可变参数列表的指针。
            va_start(ap, fmt);
            char *res; // 指向指针变量的指针，用于存储格式化字符串的结果。
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!" << std::endl;
                return;
            }
            va_end(ap); // 将ap指针置空
            serialize(LogLevel::value::FATAL, file, line, res);
            free(res); // 不释放会导致泄露
        }

    protected:
        void serialize(LogLevel::value level, const std::string &file, size_t line, char *str)
        {
            // 构造LogMsg对象
            LogMsg msg(level, line, file, _logger_name, str);
            // 格式化工具对LogMsg进行格式化 得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 进行日志落地
            log(ss.str().c_str(), ss.str().size());
        }
        /*抽象接口完成实际的落地输出*/
        virtual void log(const char *data, size_t len)
        {}

    protected:
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::value> _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };
    // 同步日志器
    class SysncLogger : public Logger
    {
    public:
        SysncLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            :Logger(logger_name, 
              level,
              formatter,
              sinks) {}
    protected:
        // 同步日志器是将日志通过落地模块句柄进行日志落地
        void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_sinks.empty()) return;
            for(auto &sink : _sinks)
            {
                sink->log(data,len);
            }
        }
    };
    // 异步日志器
    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks,AsyncLooper::AsyncType looper_type)
            :Logger(logger_name, level,formatter,sinks),
            _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog,this,std::placeholders::_1),looper_type)) {}
        void log(const char* data,size_t len)
        {
            _looper->push(data,len);
        }
        // 设计实际落地函数 将缓冲区中的数据落地
        void realLog(Buffer &buf)
        {
            if(_sinks.empty()) return;
            for(auto &sink : _sinks)
            {
                sink->log(buf.begin(),buf.readAblesize());
            }
        }
        // 
    private:
        AsyncLooper::ptr _looper;
    };

    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };
    /*
        使用建造者模式建造日志器 而不要用户直接去构造日志器 降低用户使用的复杂度

        抽象一个日志器建造者类--设置日志器类型  将不同类型日志器的创建放在同一个日志器建造类中
        派生出具体的建造者类--局部日志器的建造者 & 全局的日志器建造者
    */  

    class LoggerBuilder
    {
    public:
        LoggerBuilder():
            _logger_type(LoggerType::LOGGER_SYNC),
            _limit_level(LogLevel::value::DEBUG),
            _looper_type(AsyncLooper::AsyncType::ASYNC_SAFE) {}       
        void buildLoggerType(LoggerType type)
        {
            _logger_type = type;
        }
        void buildEnableUnSafeAsync()
        {
            _looper_type = AsyncLooper::AsyncType::ASYNC_UNSAFE;
        }
        void buildLoggerName(const std::string &name)
        {
            _logger_name = name;
        }
        void buildLoggerLevel(LogLevel::value level)
        {
            _limit_level = level;
        }
        void buildFormatter(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        template<typename SinkType , typename ...Args>
        void buildSink(Args && ...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...); //完美转发
            _sinks.push_back(psink);
        }
        virtual Logger::ptr build() = 0; // 纯虚函数
        
    protected:
        AsyncLooper::AsyncType _looper_type;
        LoggerType _logger_type;
        std::string _logger_name;
        LogLevel::value _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false); //日志器名称必须要有
            if(_formatter.get() == nullptr)
            {
                // 用户没有传入则为空 需要我们构造
                _formatter = std::make_shared<Formatter>();

            }
            if(_sinks.empty())
            {
                // 落地器为空
                buildSink<StdoutSink>();
            }
            if(_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name,_limit_level,_formatter,_sinks,_looper_type);   ;
            }
            // 不为异步则返回同步日志器
            return std::make_shared<SysncLogger>(_logger_name,_limit_level,_formatter,_sinks);           
        }
    };

    class LoggerManager
    {
    public:
        // 单例模式
        static LoggerManager& getInstance()
        {

            // 针对静态局部变量 编译器在编译的层面实现了线程安全
            // 当静态局部变量没有构造完成之前 其它的线程进入阻塞状态
            static LoggerManager eton;
            return eton;
        }
        void addLogger(Logger::ptr &logger)
        {
            if(hasLogger(logger->name())) 
                return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(),logger));

        }
        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name); //  根据日志器名字查找日志器
            if(it == _loggers.end())
            {
                return false;
            }
        }

        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock_(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end())
                return Logger::ptr();
            return it->second;
        }
        Logger::ptr rootLogger() 
        {
             return _root_logger;
        }
    private:
        LoggerManager() 
        {
            std::unique_ptr<bitlog::LoggerBuilder> builder(new bitlog::LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build(); // 默认日志器
            _loggers.insert(std::make_pair("root",_root_logger));

        }
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger; // 默认管理器
        std::unordered_map<std::string,Logger::ptr> _loggers; // 存储日志器 哈希表
    };

    // 设计一个全局日志器的建造者 在局部的基础上增加一个功能：将日志器添加到单例对象中
     class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(_logger_name.empty() == false); //日志器名称必须要有
            if(_formatter.get() == nullptr)
            {
                // 用户没有传入则为空 需要我们构造
                _formatter = std::make_shared<Formatter>();
            }
            if(_sinks.empty())
            {
                // 落地器为空
                buildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if(_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name,_limit_level,_formatter,_sinks,_looper_type);
            }
            else
            {
                logger = std::make_shared<SysncLogger>(_logger_name,_limit_level,_formatter,_sinks); 
            }
            LoggerManager::getInstance().addLogger(logger);
            return logger;
                     
        }
    };

}

#endif